#include "Thread.hpp"
#include "LockGuard.hpp"
#include <vector>
#include <queue>
#include <mutex>
#include <memory>

using namespace lcl;
// 线程池线程的数量
int gnum = 3;

template <class T>
class ThreadPool;

// 用来保存每一个线程的数据
template <class T>
class ThreadData
{
public:
    ThreadPool<T>* threadpool;
    std::string name;

public:
    ThreadData(ThreadPool<T> *tp, const std::string &n) 
    : threadpool(tp)
    , name(n)
    {}
};

template <class T>
class ThreadPool
{
private:
    // 每一个线程都会执行下面的handlerTask
    static void* handlerTask(void* args)
    {
        ThreadData<T>* td = static_cast<ThreadData<T>*>(args);
        // ThreadPool<T>* threadpool = static_cast<ThreadPool<T>*>(args);
        while(true)
        {
            T t;
            {
                LockGurad lockguard(td->threadpool->mutex());
                // 如果当前的任务队列为空，就等待
                while(td->threadpool->_task_queue.empty())
                {
                    pthread_cond_wait(td->threadpool->cond(), td->threadpool->mutex());
                }
                //t = td->threadpool->pop(); //pop的本质：将任务从公共任务队列中拿到自己当前线程的独立的栈中
                td->threadpool->pop(&t); 
            }
            // 这里注意任务的处理在解锁后，因为将任务拿到自己的独立栈后并不会影响其他线程，
            // 这样就可以解锁，让其他线程去申请锁，拿任务。
            t();
        }
        delete td;  
        return nullptr;
    }

    // 单例模式时，将构造私有化，删除拷贝和赋值
    ThreadPool(const int& num = gnum)
    :_num(gnum)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
        for (int i = 0; i < _num; i++)
        {
            _threads.push_back(new Thread());
        }
    }

    void operator=(const ThreadPool &) = delete;
    ThreadPool(const ThreadPool &) = delete;

public:

    // static ThreadPool<T>* getInstance()
    // {
    //     if(_tp == nullptr) // 双重判定空指针, 降低锁冲突的概率, 提高性能
    //     {
    //         _singlock.lock(); // 使用互斥锁, 保证多线程情况下也只调用一次new
    //         if(_tp == nullptr)
    //         {
    //             _tp = new ThreadPool<T>(); // 如果在new时抛异常，就可能出现死锁的问题。
    //         }
    //         _singlock.unlock();
    //     }
    //     return _tp;
    // }
    
    static ThreadPool<T>* getInstance()
    {
        if(_tp == nullptr) // 双重判定空指针, 降低锁冲突的概率, 提高性能
        {
            std::lock_guard<std::mutex> lock(_singlock); // 使用互斥锁, 保证多线程情况下也只调用一次new
            if(_tp == nullptr)
            {
                _tp = new ThreadPool<T>();
            }
        }
        return _tp;
    }
    // 将创建的线程池启动
    void run()
    {
        for(const auto& t : _threads)
        {
            // 这里的每一个t都是一个线程对象，Thread
            ThreadData<T>* td = new ThreadData<T>(this, t->threadname());
            t->start(handlerTask, (void*)td);
            std::cout << t->threadname() << " start ..." << std::endl;
        }
    }
    // 向线程池push任务
    void push(const T& in)
    {
        // 向线程池对应的任务队列中push
        // pthread_mutex_lock(&_mutex);
        LockGurad lockguard(&_mutex);
        _task_queue.push(in);
        pthread_cond_signal(&_cond);
        // pthread_mutex_unlock(&_mutex);
    }

    void pop(T* out)
    {
        // 注意这里不能再加锁，因为上面执行pop时已经加锁了，就是在临界区操作的。
        // LockGurad lockguard(&_mutex);
        *out = _task_queue.front();
        _task_queue.pop();
    }

    // T pop()
    // {
    //     T t = _task_queue.front();
    //     _task_queue.pop();
    //     return t;
    // }

    ~ThreadPool()
    {
        for(auto& t : _threads)
        {
            delete t;
        }

        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

public:
    pthread_mutex_t* mutex()
    {
        return &_mutex;
    }

    pthread_cond_t* cond()
    {
        return &_cond;
    }

private:
    int _num;
    std::vector<Thread*> _threads; // 保存创建出来的一批线程，都去task_queue中等待任务
    std::queue<T> _task_queue; // 看到的公共资源，需要对公共资源进行保护
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;
    // 单例模式，为了只有一个对象，所以设置为静态的
    static ThreadPool<T>* _tp;
    static std::mutex _singlock; // C++11中的锁
};

template <class T>
ThreadPool<T>* ThreadPool<T>::_tp = nullptr;

template <class T>
std::mutex ThreadPool<T>::_singlock;